/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.util;

import static ch.trackedbean.utils.BeanTestUtil.*;
import static junit.framework.Assert.*;
import java.util.*;
import org.junit.*;
import ch.trackedbean.copier.*;
import ch.trackedbean.data.*;

/**
 * Test for the utility package.
 * 
 * @author M. Hautle
 */
public class GeneralUtils {
	/**
	 * Test for {@link BeanUtils#copyValues(Object, Object)}.
	 * 
	 * @throws Exception
	 */
	@Test
	public void testFlatCopy() throws Exception {
		// simple
		final DomA src = DomFactory.createValidDomA();
		final DomA dst = new DomA();
		BeanUtils.copyValues(src, dst);
		checkBeans("Flat copy was not successfull", src, dst);
		// with inheritance
		final DomB src1 = DomFactory.createValidDomB();
		final DomB dst1 = new DomB();
		BeanUtils.copyValues(src1, dst1);
		checkBeans("Flat copy was not successfull", src1, dst1);
	}

	/**
	 * Test for {@link BeanUtils#copyValuesTo(Object, Object, String...)}.
	 */
	@Test
	public void testFlatCopyOnDifferentTypes() throws Exception {
		// simple
		final DomA src = DomFactory.createValidDomA();
		final TOA dst = BeanUtils.copyValuesTo(src, new TOA(), DomA.ATTR_NUMBER);
		final DomA res = BeanMapperManager.copyCompleteDst2Src(dst, new DomA());
		checkBeans("Flat copy was not successfull", src, res, TOA.ATTR_NUMBER);
		final TOA dst2 = BeanUtils.copyValuesTo(src, new TOA(), DomA.ATTR_TEXT_A, DomA.ATTR_NUMBER);
		assertNotNull("Ignored property was copied", dst.getTextA());
	}

	/**
	 * Test for {@link BeanCloner#cloneBean(Object)}.
	 * 
	 * @throws Exception
	 */
	@Test
	public void testBeanClone() throws Exception {
		// simple
		final DomA srcA = DomFactory.createValidDomA();
		final DomB srcB = DomFactory.createValidDomB();
		final DomC srcC = DomFactory.createValidDomC();
		final DomD srcD = DomFactory.createValidDomD();
		checkBeans("A clone failed", srcA, BeanCloner.cloneBean(srcA));
		checkBeans("B clone failed", srcB, BeanCloner.cloneBean(srcB));
		checkBeans("C clone failed", srcC, BeanCloner.cloneBean(srcC));
		final DomD cloneD = BeanCloner.cloneBean(srcD);
		checkProperties("D clone failed", srcD.getOtherDom(), cloneD.getOtherDom());
		checkProperties("D clone failed", srcD.getSubDom().getOtherDom(), cloneD.getSubDom().getOtherDom());
	}

	/**
	 * Test for {@link ElementValueComparator}.
	 * 
	 * @throws Exception
	 */
	@Test
	public void testComparator() throws Exception {
		final List<DomA> src = new ArrayList<DomA>();
		src.add(DomFactory.fillDomA(new DomA(), 1, "g", "a", 1));
		src.add(DomFactory.fillDomA(new DomA(), 1, "z", "a", 1));
		src.add(DomFactory.fillDomA(new DomA(), 2, "e", "a", 1));
		src.add(DomFactory.fillDomA(new DomA(), 2, "f", "a", 1));
		src.add(DomFactory.fillDomA(new DomA(), 3, "a", "a", 1));
		src.add(DomFactory.fillDomA(new DomA(), 3, "b", "a", 1));
		final List<DomA> dst = new ArrayList<DomA>(src);
		final ElementValueComparator<DomA> cmp = new ElementValueComparator<DomA>(DomA.class, DomA.ATTR_NUMBER_A, DomA.ATTR_GETTER_A);
		Collections.shuffle(dst);
		differentOrder("Shuffling failed - rerun the test", src, dst);
		Collections.sort(dst, cmp);
		equalOrder(src, dst);
		Collections.reverse(src);
		Collections.shuffle(dst);
		differentOrder("Shuffling failed - rerun the test", src, dst);
		cmp.setOrder(false);
		Collections.sort(dst, cmp);
		equalOrder(src, dst);
	}

	/**
	 * General test for {@link ELExpression}.
	 */
	@Test
	public void testELExpression() {
		final String undefined = "Nüt";
		final String simple = "Foo";
		final String getter = "Bar";
		final String delim = " - ";
		final String end = " Trailer";
		final DomB bean = new DomB();
		final DomB second = new DomB();
		second.setSimple(simple);
		second.setGetterA(getter);
		bean.setListB(Arrays.asList(new DomA(), second));
		final ELExpression exp = new ELExpression("${" + DomB.ATTR_LIST_B + "[1]." + DomB.ATTR_SIMPLE + "}" + delim + "${" + DomB.ATTR_LIST_B + "[1]."
				+ DomB.ATTR_GETTER_A + "}" + end, undefined);
		assertEquals(undefined + delim + undefined + end, exp.evaluate(new DomB()));
		assertEquals(undefined + delim + undefined + end, exp.evaluate(null));
		assertEquals(simple + delim + getter + end, exp.evaluate(bean));
	}

	/**
	 * Reading test for {@link ELHelper#getValueOf(String, Object, Object)} and {@link ELAccessor#getValue(Object, Object)}.
	 */
	@Test
	public void testELRead() {
		final String undefined = "Nüt";
		final String simple = "Foo";
		final String propertyEl = DomB.ATTR_LIST_B + "[1]." + DomB.ATTR_SIMPLE;
		final DomB bean = new DomB();
		final DomB second = new DomB();
		final ELAccessor acc = new ELAccessor(propertyEl, Object.class);
		second.setSimple(simple);
		bean.setListB(Arrays.asList(new DomA(), second));
		assertSame("ELHelper: null check failed", undefined, ELHelper.getValueOf(propertyEl, null, undefined));
		assertSame("ELHelper: empty check failed", undefined, ELHelper.getValueOf(propertyEl, new DomB(), undefined));
		assertSame("ELHelper: access check failed", simple, ELHelper.getValueOf(propertyEl, bean, undefined));
		assertSame("ELAccessor: null check failed", undefined, acc.getValue(null, undefined));
		assertSame("ELAccessor: empty check failed", undefined, acc.getValue(new DomB(), undefined));
		assertSame("ELAccessor: access check failed", simple, acc.getValue(bean, undefined));
	}

	/**
	 * List access test for {@link ELHelper#getValueOf(String, Object, Object)} and {@link ELAccessor#getValue(Object, Object)}.
	 */
	@Test
	public void testELReadList() {
		final String undefined = "Nüt";
		final String simple = "Foo";
		final String arrayEl = DomB.ATTR_LIST_B + "[1]";
		final DomB bean = new DomB();
		final DomB second = new DomB();
		final ELAccessor acc = new ELAccessor(arrayEl, Object.class);
		second.setSimple(simple);
		bean.setListB(Arrays.asList(new DomA(), second));
		assertSame("ELHelper: null check failed", undefined, ELHelper.getValueOf(arrayEl, null, undefined));
		assertSame("ELHelper: empty check failed", undefined, ELHelper.getValueOf(arrayEl, new DomB(), undefined));
		assertSame("ELHelper: access check failed", second, ELHelper.getValueOf(arrayEl, bean, undefined));
		assertSame("ELAccessor: null check failed", undefined, acc.getValue(null, undefined));
		assertSame("ELAccessor: empty check failed", undefined, acc.getValue(new DomB(), undefined));
		assertSame("ELAccessor: access check failed", second, acc.getValue(bean, undefined));
	}

	/**
	 * General test for {@link ELHelper#setValueOf(String, Object, Object)} and {@link ELAccessor#setValue(Object, Object)}
	 */
	@Test
	public void testELWrite() {
		final String simple = "Foo";
		final String simpleNew = "Bar";
		final String el = DomB.ATTR_LIST_B + "[1]." + DomB.ATTR_SIMPLE;
		final DomB bean = new DomB();
		final DomB second = new DomB();
		final ELAccessor acc = new ELAccessor(el, Object.class);
		second.setSimple(simple);
		bean.setListB(Arrays.asList(new DomA(), second));
		assertFalse("Helper had written something?", ELHelper.setValueOf(el, new DomB(), simpleNew));
		assertTrue("Helper did not wrote", ELHelper.setValueOf(el, bean, simpleNew));
		assertSame("Helper writing was not successfull", simpleNew, second.getSimple());
		assertFalse("Accessor had written something?", acc.setValue(new DomB(), simpleNew));
		assertTrue("Accessor did not wrote", acc.setValue(bean, simpleNew));
		assertSame("Accessor writing was not successfull", simpleNew, second.getSimple());
	}

	/**
	 * List write test for {@link ELHelper#setValueOf(String, Object, Object)} and {@link ELAccessor#setValue(Object, Object)}
	 */
	@Test
	public void testELListWrite() {
		final String simple = "Foo";
		final String arrayEl = DomB.ATTR_LIST_B + "[1]";
		final DomB bean = new DomB();
		final DomB second = new DomB();
		final DomB value = new DomB();
		final ELAccessor acc = new ELAccessor(arrayEl, Object.class);
		second.setSimple(simple);
		bean.setListB(new ArrayList(Arrays.asList(new DomA(), second)));
		assertFalse("Helper had written something?", ELHelper.setValueOf(arrayEl, new DomB(), value));
		assertTrue("Helper did not wrote", ELHelper.setValueOf(arrayEl, bean, value));
		assertSame("Helper writing was not successfull", value, bean.getListB().get(1));
		assertFalse("Accessor had written something?", acc.setValue(new DomB(), value));
		assertTrue("Accessor did not wrote", acc.setValue(bean, value));
		assertSame("Accessor writing was not successfull", value, bean.getListB().get(1));
	}

	/**
	 * Ensures that the given lists have the same instances at the same place.
	 * 
	 * @param <T> The type
	 * @param src The source list
	 * @param dst The destination list
	 */
	private <T> void equalOrder(List<T> src, List<T> dst) {
		assertTrue(src.size() == dst.size());
		for (int i = 0, cnt = src.size(); i < cnt; i++) {
			assertSame(src.get(i), dst.get(i));
		}
	}

	/**
	 * Ensures that the given lists have not all instances at the same places.
	 * 
	 * @param <T> The type
	 * @param msg The message in case of an error
	 * @param src The source list
	 * @param dst The destination list
	 */
	private <T> void differentOrder(String msg, List<T> src, List<T> dst) {
		assertTrue(src.size() == dst.size());
		boolean same = true;
		for (int i = 0, cnt = src.size(); i < cnt; i++) {
			same &= src.get(i) == dst.get(i);
		}
		assertFalse(msg, same);
	}

	/**
	 * Cleans up
	 * 
	 * @throws Exception
	 */
	@After
	public void tearDown() throws Exception {
		BeanUtils.clearCache();
	}
}
